﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using Main.PrimitivesCommunication;
using System.IO;
using System.Threading;
using Main.Packets;
using Newtonsoft.Json;

namespace Main.Reseau
{
    class CoucheReseau
    {
        private const int MULTIPLE_POUR_REFUS_CONNEXION_FOURNISSEUR = 27;
        private const int MULTIPLE_POUR_REFUS_CONNEXION_DISTANT = 13;
        private const int MULTIPLE_POUR_ABSENCE_REPONSE_CONNEXION = 19;
        private const int MULTIPLE_POUR_ABSENCE_ACQUITTEMENT = 15;

        NamedPipeClientStream reseauToTransportPipe;
        StreamWriter swReseauToTransport;

        public void LireDeTransport()
        {
            reseauToTransportPipe = new NamedPipeClientStream(REGLES.PIPE_RESEAU_TO_TRANSPORT);
            swReseauToTransport = new StreamWriter(reseauToTransportPipe);
            
            reseauToTransportPipe.Connect();
            swReseauToTransport.AutoFlush = true;

            NamedPipeClientStream transportToReseauPipe = new NamedPipeClientStream(REGLES.PIPE_TRANSPORT_TO_RESEAU);
            NamedPipeServerStream reseauToReseauPipe = new NamedPipeServerStream(REGLES.PIPE_RESEAU_TO_RESEAU, PipeDirection.InOut, 10, PipeTransmissionMode.Message);
            //Console.WriteLine(((PipeStream)transportToReseauPipe).IsAsync.ToString());
            StreamReader sr = new StreamReader(transportToReseauPipe);
            StreamWriter swrr = new StreamWriter(reseauToReseauPipe);

            reseauToReseauPipe.WaitForConnection();
            transportToReseauPipe.Connect();

            transportToReseauPipe.ReadMode = PipeTransmissionMode.Message;

            Packet packetToSend = null;
            Primitive primitiveReponse;
            Connexion connexion;
            while (true)
            {
                //string test = sr.ReadToEnd();
                Primitive pr = Primitive.ReadJson(sr);

                primitiveReponse = null;
                switch (pr.Primitives)
                {
                    case Primitives.NConnectRequest:
                        if (ConnexionAcceptee(pr.AdresseSource))
                        {
                            connexion = Connexion.CreateConnexion(pr.AdresseSource, pr.AdresseDestination);
                            packetToSend = ConnectionPacket.CreateRequestConnectionPacket(connexion.IdConnexionLogique, connexion.AdresseSource, connexion.AdresseDestination);
                            EnvoieReseauVersLiaison(packetToSend);
                            Packet packetReponse = EnvoieLiaisonVersReseau(packetToSend);
                            if (packetReponse != null)
                            {
                                switch (packetReponse.PacketType.Type)
                                {
                                    case PacketTypes.ConnectionConfirmationPacket:
                                        primitiveReponse = Primitive.CreateNConnectConf(((ConnectionPacket)packetReponse).AdresseDestination);
                                        break;
                                    case PacketTypes.FreeConnectionPacket:
                                        primitiveReponse = Primitive.CreateNDisconnectInd(((FreeConnectionPacket)packetReponse).AdresseDestination, ((FreeConnectionPacket)packetReponse).Raison);
                                        break;
                                }
                            }
                        }
                        else
                        {
                            primitiveReponse = Primitive.CreateNDisconnectInd(pr.AdresseSource, Raisons.FournisseurRefuse);

                        }
                        break;

                    case Primitives.NDataRequest:
                        break;

                    case Primitives.NDisconnectRequest:
                        break;
                }
                if (packetToSend != null)
                    packetToSend.WriteJson(swrr);

                //EnvoieReseauVersLiaison(packetToSend);
            }

            //transportToReseauPipe.Close();
        }

        public void EcrireVersTransport()
        {
            NamedPipeClientStream reseauToReseauPipe = new NamedPipeClientStream(REGLES.PIPE_RESEAU_TO_RESEAU);
            NamedPipeServerStream reseauToTransportPipe = new NamedPipeServerStream(REGLES.PIPE_RESEAU_TO_TRANSPORT,PipeDirection.InOut,10,PipeTransmissionMode.Message);

            StreamReader srrr = new StreamReader(reseauToReseauPipe);
            StreamWriter swrt = new StreamWriter(reseauToTransportPipe);

            reseauToReseauPipe.Connect();
            reseauToTransportPipe.WaitForConnection();

            reseauToReseauPipe.ReadMode = PipeTransmissionMode.Message;
            reseauToTransportPipe.ReadMode = PipeTransmissionMode.Message;
            

            Packet packet = Packet.ReadJson(srrr);
            Primitive primitiveReponse = null;

            switch (packet.PacketType.Type)
            {
                case PacketTypes.ConnectionConfirmationPacket:
                    primitiveReponse = Primitive.CreateNConnectConf(((ConnectionPacket)packet).AdresseDestination);
                    break;
                case PacketTypes.FreeConnectionPacket:
                    primitiveReponse = Primitive.CreateNDisconnectInd(((FreeConnectionPacket)packet).AdresseDestination, ((FreeConnectionPacket)packet).Raison);
                    break;
                default:
                    throw new NotImplementedException();
            }

            primitiveReponse.WriteJson(swrt);
        }

        private void EnvoieReseauVersLiaison(Packet p)
        {
            //Simulation de ce que Réseau réussi à envoyer à la couche liaison
            using (StreamWriter sw = new StreamWriter(REGLES.L_ECRITURE, true))
            {
                p.WriteJson(sw);
            }
        }

        private void EnvoieSystemeBVersLiaison(Packet p)
        {
            using (StreamWriter sw = new StreamWriter(REGLES.L_LECTURE, true))
            {
                p.WriteJson(sw);
            }
        }

        private Packet EnvoieLiaisonVersReseau(Packet packetRecu)
        {
            Packet packetEnvoye = null;
            switch (packetRecu.PacketType.Type)
            {
                case PacketTypes.ConnectionRequestPacket:
                    ConnectionPacket connectionPacket = (ConnectionPacket)packetRecu;
                    if ((connectionPacket.AdresseSource % MULTIPLE_POUR_REFUS_CONNEXION_DISTANT) == 0)
                    {
                        FreeConnectionPacket packetReponse = FreeConnectionPacket.CreateFreeConnectionIndicationPacket(connectionPacket.NumeroConnexion, connectionPacket.AdresseDestination, connectionPacket.AdresseSource, Raisons.DistantRefuse);
                        packetEnvoye = packetReponse;
                        EnvoieSystemeBVersLiaison(packetReponse);
                    }
                    else if (!((connectionPacket.AdresseSource % MULTIPLE_POUR_ABSENCE_REPONSE_CONNEXION) == 0))
                    {
                        ConnectionPacket packetReponse = ConnectionPacket.CreateConnectionConfirmationPacket(connectionPacket.NumeroConnexion, connectionPacket.AdresseDestination, connectionPacket.AdresseSource);
                        packetEnvoye = packetReponse;
                        EnvoieSystemeBVersLiaison(packetReponse);
                    }
                    break;

                case PacketTypes.DataPacket:
                    if (AquittementNegatif(2))
                    {

                    }
                    break;
            }
            return packetEnvoye;
        }

        private bool ConnexionAcceptee(byte adresseSource)
        {
            return !((adresseSource % MULTIPLE_POUR_REFUS_CONNEXION_FOURNISSEUR) == 0);
        }

        private bool AquittementNegatif(int ps)
        {
            Random rand = new Random();
            return (ps == rand.Next(0, 7));
        }

    }
}
